package com.ts.main.mini.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.ts.common.utils.HttpServletUtils;
import com.ts.main.mini.config.JsapiConfig;
import com.ts.main.mini.dto.WechatPayDto;
import com.ts.main.mini.enums.OrderStatusEnum;
import com.ts.main.mini.exception.CommonException;
import com.ts.main.mini.properties.WxPayProperties;
import com.ts.main.mini.vo.OrderVo;
import com.ts.main.mini.vo.TransactionVo;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author:lk
 * @Date: 2025/5/27  00:35
 */
@Slf4j
@AllArgsConstructor
@Service
public class WechatPayService {

    private final WxPayProperties wxPayProperties;
    private final OrderServiceImpl orderService;
    private final JsapiConfig jsapiConfig;
    private final ReentrantLock lock = new ReentrantLock(true);

    /**
     * 创建预支付订单
     *
     * @param dto
     * @return
     */
    public PrepayWithRequestPaymentResponse generateParams(WechatPayDto dto) {
        if(lock.tryLock()){
            try {
                //支付数据校验
                createWechatPay(dto);
                JsapiServiceExtension service = new JsapiServiceExtension
                        .Builder()
                        .config(jsapiConfig.initConfig())
                        .signType("RSA")
                        .build();
                //预支付请求参数
                BigDecimal totalAmount = new BigDecimal(dto.getAmount())
                        .multiply(new BigDecimal(100))
                        .setScale(0, RoundingMode.HALF_UP);
                PrepayRequest request = new PrepayRequest();
                Amount amount = new Amount();
                amount.setTotal(totalAmount.intValue());          //单位：分
                amount.setCurrency("CNY");                        //RMB
                request.setAmount(amount);                        //总金额
                request.setAppid(wxPayProperties.getAppid());                          //小程序id
                request.setMchid(wxPayProperties.getMerchantId());                     //商户号id
                request.setDescription(dto.getProductInfo());     //商品信息
                request.setNotifyUrl(wxPayProperties.getNotifyUrl());                  //回调地址
                request.setOutTradeNo(dto.getOrderNumber());      //订单号
                //request.setTimeExpire(formattedTime);           //过期时间
                //购买者信息
                Payer payer = new Payer();
                payer.setOpenid(dto.getOpenid());
                request.setPayer(payer);
                try {
                    //调用接口
                    PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
                    System.out.println(response);
                    return response;
                    //ValidationException ：当验证微信支付签名失败时抛出该异常。
                } catch (HttpException e) { // 发送HTTP请求失败
                    // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
                    throw new RuntimeException("构建请求参数失败" + e.getHttpRequest());
                } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
                    // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
                    throw new RuntimeException("调用微信支付服务失败" + e.getErrorCode() + e.getErrorMessage());
                } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
                    // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
                    throw new RuntimeException("获取支付参数失败" + e.getMessage());
                }
            }finally {
                lock.unlock();
            }
        }else {
            log.info("当前业务繁忙，请稍后重试");
            throw new CommonException("当前业务繁忙，请稍后重试");
        }
    }

    /**
     * 支付数据校验
     *
     * @param dto
     * @return
     */
    public void createWechatPay(WechatPayDto dto) {
        // 系统订单支付状态
        OrderVo orderVo = orderService.getOrderByOrderNumber(dto.getOrderNumber());
        if (ObjectUtil.isEmpty(orderVo))
            throw new CommonException("订单不存在");
        if (!orderVo.getOrderStatus().equals(OrderStatusEnum.OBLIGATION.getTypeValue()))
            throw new CommonException("订单已支付,请勿重复操作");
        // 校验参数合法性
        if (Double.parseDouble(dto.getAmount()) <= 0) {
            throw new CommonException("金额必须大于0");
        }
    }


    /***
     * 微信支付回调通知
     * @param request
     * @return
     * @throws IOException
     */
    @Transactional
    public synchronized String payNotify(HttpServletRequest request) {
        log.info("------收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = null;
        try {
            requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .signType(signType)
                    .body(HttpServletUtils.getRequestBody(request))
                    .build();
        } catch (IOException e) {
            throw new CommonException("参数构建失败");
        }

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(jsapiConfig.initNotificationConfig());

        // 以支付通知回调为例，验签、解密并转换成 Transaction
        log.info("验签参数：{}", requestParam);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("验签成功！-支付回调结果：{}", transaction.toString());

        Map<String, String> returnMap = new HashMap<>(2);

        //支付失败
        if (!transaction.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
            returnMap.put("code", "FAIL");
            returnMap.put("message", "失败");
        } else {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", transaction.getOutTradeNo(), transaction.getTransactionId());
            returnMap.put("code", "SUCCESS");
            returnMap.put("message", "成功");
            //修改订单前，建议主动请求微信查询订单是否支付成功，防止恶意post
            TransactionVo transaction1 = queryOrderByOutTradeNo(transaction.getOutTradeNo());
            if (transaction1.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
                //支付金额
                Integer total = transaction.getAmount().getPayerTotal();
                BigDecimal divide = new BigDecimal(total)
                        .divide(new BigDecimal(100))
                        .setScale(2, RoundingMode.HALF_UP);
                //修改订单信息
                orderService.updateOrderStausByNumber(
                        transaction.getOutTradeNo(),
                        transaction.getPayer().getOpenid(),
                        String.valueOf(divide),
                        OrderStatusEnum.WAIT_DELIVER.getTypeValue(),
                        OrderStatusEnum.WAIT_DELIVER.getTypeOrder());
            }
        }
        return JSONObject.toJSONString(returnMap);
    }

    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo
     * @return
     */
    public TransactionVo queryOrderByOutTradeNo(String outTradeNo) {
        //初始化服务
        JsapiServiceExtension service = new JsapiServiceExtension
                .Builder()
                .config(jsapiConfig.initConfig())
                .build();
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(outTradeNo);
        request.setMchid(wxPayProperties.getMerchantId());
        Transaction transaction = service.queryOrderByOutTradeNo(request);
        return BeanUtil.copyProperties(transaction, TransactionVo.class);
    }

    /**
     * 微信支付订单号查询订单(微信支付订单号)
     *
     * @param transactionId
     * @return
     */
    public TransactionVo queryOrderById(String transactionId) {
        //初始化服务
        JsapiServiceExtension service = new JsapiServiceExtension
                .Builder()
                .config(jsapiConfig.initConfig())
                .build();
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayProperties.getMerchantId());
        queryRequest.setTransactionId(transactionId);
        Transaction transaction = service.queryOrderById(queryRequest);
        return BeanUtil.copyProperties(transaction, TransactionVo.class);
    }

    /**
     * 取消支付（关闭支付订单）
     *
     * @param orderNumber
     */
    public void closeOrder(String orderNumber) {
        if(lock.tryLock()){
            try {
                //初始化服务
                JsapiServiceExtension service = new JsapiServiceExtension
                        .Builder()
                        .config(jsapiConfig.initConfig())
                        .build();

                CloseOrderRequest request = new CloseOrderRequest();
                request.setMchid(wxPayProperties.getMerchantId());
                request.setOutTradeNo(orderNumber);
                service.closeOrder(request);
            }finally {
                lock.unlock();
            }
        }
    }

    /**
     * 微信申请退款
     *
     * @param orderNumber 商户订单号
     * @param totalAmount 退款金额，单位分
     * @return
     */
    public Boolean refundOrder(String orderNumber, String totalAmount) {
        if(lock.tryLock()){
            try {
                // 构建退款service
                RefundService service = new RefundService.Builder()
                        .config(jsapiConfig.initConfig())  // 配置微信支付
                        .build();
                // 创建退款请求
                CreateRequest request = new CreateRequest();
                request.setOutTradeNo(orderNumber);               // 设置商户订单号
                request.setOutRefundNo("REFUND_" + orderNumber);  // 设置退款订单号

                // 设置退款金额
                BigDecimal totalAmountNew = new BigDecimal(totalAmount)
                        .multiply(new BigDecimal(100))
                        .setScale(0, RoundingMode.HALF_UP);
                log.info("totalAmountNew{}", totalAmountNew);
                AmountReq amount = new AmountReq();
                long totalAmountLong = totalAmountNew.longValue(); // 获取总金额（分）
                amount.setTotal(totalAmountLong);  // 总金额
                amount.setRefund(totalAmountLong); // 退款金额
                amount.setCurrency("CNY");         // 货币单位，人民币

                request.setAmount(amount);
                request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());  // 设置退款通知URL

                // 执行退款请求
                Refund refund = service.create(request);
                log.info("退款返回信息：{}", refund);

                // 根据退款状态处理不同情况
                switch (refund.getStatus()) {
                    case Status.SUCCESS:
                        log.info("退款成功，交易号：{}", refund.getTransactionId());
                        return true;
                    case Status.ABNORMAL:
                        log.error("退款失败，账户异常，订单号：{}", orderNumber);
                        throw new CommonException("退款失败，账户异常！");
                    case Status.PROCESSING:
                        log.warn("退款处理中，订单号：{}", orderNumber);
                        throw new CommonException("退款正在处理中！");
                    case Status.CLOSED:
                        log.error("退款失败，余额不足或订单已关闭，订单号：{}", orderNumber);
                        throw new CommonException("退款失败，用户余额不足或订单超过退款期限！");
                    default:
                        log.error("退款失败，未知错误，订单号：{}", orderNumber);
                        throw new CommonException("退款失败，未知错误！");
                }
            } catch (ServiceException e) {
                // 处理微信支付服务异常
                log.error("微信退款失败，错误信息：{}", e.getErrorMessage());
                throw new CommonException("退款失败！" + e.getErrorMessage());
            } catch (NumberFormatException e) {
                // 捕获格式化错误，处理金额转换问题
                log.error("退款金额格式错误，错误信息：{}", e.getMessage());
                throw new CommonException("退款金额格式错误！");
            } catch (Exception e) {
                // 处理其他非预期异常
                log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());
                throw new CommonException("退款失败！" + e.getMessage());
            }finally {
                lock.unlock();
            }
        }else {
            log.info("当前业务繁忙，请稍后重试");
            throw new CommonException("当前业务繁忙，请稍后重试");
        }
    }


    /**
     * 微信退款回调处理
     *
     * @param request 请求对象
     */
    public void refundNotify(HttpServletRequest request) {
        try {
            log.info("------ 收到退款通知 ------");
            // 获取请求头信息
            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String serial = request.getHeader("Wechatpay-Serial");
            String signType = request.getHeader("Wechatpay-Signature-Type");
            // 构造请求参数对象
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .signType(signType)
                    .body(HttpServletUtils.getRequestBody(request))
                    .build();
            // 初始化通知解析器
            NotificationParser parser = new NotificationParser(jsapiConfig.initNotificationConfig());
            // 验签并解析支付通知
            log.info("验签参数：{}", requestParam);
            RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
            log.info("验签成功！退款回调结果：{}", parse);
            // 获取退款状态并记录日志
            String refundStatus = parse.getRefundStatus().toString();
            log.info("退款状态：{}", refundStatus);
            // 更新订单状态为已取消
            orderService.updateOrderStausByNumber(
                    parse.getOutTradeNo(),
                    OrderStatusEnum.CANCELLED.getTypeValue(),
                    OrderStatusEnum.CANCELLED.getTypeOrder()
            );
            // 处理退款成功的情况
            if ("SUCCESS".equals(refundStatus)) {
                log.info("退款成功，进入回调，状态：{}", refundStatus);
            } else {
                log.warn("退款未成功，退款状态：{}", refundStatus);
            }
        } catch (Exception e) {
            log.error("退款回调失败！错误信息：{}", e.getMessage(), e);
            // 可根据需求抛出特定的异常
        }
    }

}

